﻿/// <reference path="../mapparola.core/definitions/node-0.11.d.ts" />
/// <reference path="../mapparola.core/definitions/ws.d.ts" />
/// <reference path="../mapparola.core/definitions/mysql.d.ts" />
/// <reference path="../mapparola.core/mapparolacore.d.ts" />

module Mapparola.Server {

    export class ServerApp {
        private WebSocketServer = require('ws').Server;
        private mysql: mysql.IMySql = require('mysql');
        private webSocket;
        private db: mysql.IConnection;
        private wss;

        constructor() {
            this.wss = new this.WebSocketServer({ port: 8081 });
            this.db = this.mysql.createConnection({
                host: 'localhost',
                user: 'root',
                password: 'secret'
            });

            //defining custom query format
            this.db.config.queryFormat = (query, values) => {
                if (!values) {
                    return query;
                }
                return query.replace(/\:(\w+)/g, (txt: string, key: string) => {
                    if (values.hasOwnProperty(key)) {
                        return this.db.escape(values[key]);
                    }
                    return txt;
                });
            };

            this.db.connect( (err: mysql.IError) => {
                if (err) {
                    console.error('error connecting: ' + err.stack);
                    return;
                }

                console.log('connected as id ' + this.db.threadId);
            });

            this.wss.on('connection', (ws : any) => {
                this.webSocket = ws;
                console.log('A new member is here, yo!');
                ws.on('message', (message : string) => {
                    try {
                        var parsedMessage = JSON.parse(message);
                        if (parsedMessage.type === 'POST') {
                            this.SaveToMessageToDatabase(parsedMessage);
                        } else if (parsedMessage.type === 'GET') {
                            this.GetMessagesAroundPositionFromDatabase(parsedMessage);
                        } else {
                            console.log("Message is neither POST nor GET");
                        }
                    } catch (e) {
                        console.log('Something bad happened:' + e);
                    }
                });
            });
        }

        private generateToken() : string {
            return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
                var r : number = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
                return v.toString(16);
            });
        }

        private CreateUser(user: Mapparola.Core.User) {
            this.db.query(
                'INSERT INTO mapparola_model.t_accounts SET token = ?, username = ?, password = ?',
                [this.generateToken(), user.username, user.password],
                (err: mysql.IError, results: any[]) => {
                    if (err) {
                        throw err;
                    }
                    console.log("User " + user.username + "/" + user.password +"created!");
                });
        }

        public LogUserIn(user : Mapparola.Core.User) {
            console.log("Log in request received for username '" + user.username + "' and password '" + user.password + "'");

            this.db.query(
                'SELECT token FROM mapparola_model.t_accounts WHERE username = :username AND password = :password',
                {
                    username: user.username,
                    password: user.password
                },
                (err: mysql.IError, result: any[]) => {
                    if (err) {
                        throw err;
                    }

                    if (result.length > 0) {
                        console.log("User exists, YO!")
                    } else {
                        console.log("The user doesn't exist, creating one.");
                        var tmpUser: Mapparola.Core.User ={
                            username: user.username,
                            password: user.password
                        };

                        this.CreateUser(tmpUser);
                    }
                });
        }

        private SaveToMessageToDatabase(message: Mapparola.Core.MapparolaMessage): void {
            console.log("POST received with following message: " + message);

            if (message.location.y && message.location.x && message.text) {
                var lat: number = message.location.y;
                var lng: number = message.location.x;
                var msg: string = message.text;
                var token: string = this.generateToken();

                this.db.query(
                    'INSERT INTO mapparola_model.t_messages SET message = ?, token = ?, location = GeomFromText(?)',
                    [msg, token, 'POINT(' + lng + ' ' + lat + ')'],
                    (err: mysql.IError, result: any[]) => {
                        if (err) {
                            throw err;
                        }
                        console.log("A new record has been created in the database  with token " + token);
                        return true;
                    });
            } else {
                console.log("Something is missing from message to save.");
            }
        }

        public GetMessagesAroundPositionFromDatabase(message) {
            console.log("GET received with following message: " + message);

            if (message.location.y && message.location.x) {
                var lat : number = message.location.y;
                var lng : number = message.location.x;
                this.db.query(
                    'SELECT message, location FROM mapparolascema.maintable WHERE MBRContains( LineString ( Point ( ' + lng + ' + 5 / ( 111.1 / COS(RADIANS(' + lat + '))), ' + lat + ' + 5 / 111.1), Point ( ' + lng + ' - 5 / ( 111.1 / COS(RADIANS(' + lat + '))), ' + lat + ' - 5 / 111.1) ), location)'
                    , function (err, result) {
                        if (err) throw err;
                        console.log(result);
                        if (this.webSocket) {
                            this.webSocket.send(result ? JSON.stringify(result) : JSON.stringify("{message:suca}"));
                        } else {
                            console.log('no ws');
                        }
                    });
            } else {
                console.log("Something is missing from message to get.");
            }
        }

    }
}

var server = new Mapparola.Server.ServerApp();

var user: Mapparola.Core.User = {
    username: "test",
    password: "password"
};

var getRequest: Mapparola.Core.MapparolaMessage = {
    location: {
        y: 48.431855,
        x: -123.391995
    },
    type: "GET"
};

//server.LogUserIn(user);
//server.GetMessagesAroundPositionFromDatabase(getRequest);


//SaveToMessageToDatabase(JSON.parse('{"location": { "y": "-123.393098", "x": "48.431325" } ,"message":"ca1412313511738"}'));
//GetMessagesAroundPositionFromDatabase(JSON.parse('{"location": {"y":48.431855,"x":-123.391995}}'));
